package com.ruoyi.flow.flow.task;

import com.ruoyi.flow.common.Page;
import com.ruoyi.flow.core.exception.BadRequestException;
import com.ruoyi.flow.flow.FlowConstants;
import com.ruoyi.flow.flow.workflow.*;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.DelegationState;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;

/**
 * Created by fenghuo on 2018/8/25.
 */
@Service
public class FlowTaskService {
    private static final Logger LOGGER = LoggerFactory.getLogger(FlowTaskService.class);
    @Autowired
    private HistoryService historyService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RuntimeService runtimeService;

    /**
     * 获取我的已办任务
     *
     * @param userId
     * @param page
     * @return
     */
    public Page<FlowTask> getDoneTasks(String userId, Page<FlowTask> page) {
        List<HistoricTaskInstance> historicTaskInstanceList = null;
        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).finished();
        if (page == null) {
            page = new Page<>();
            page.setOrder(Page.ORDER_DIRECTION_ASC);
            page.setSort(Page.DEFAULT_SORT);
        }
        //执行排序
        historicTaskInstanceQuery = page.executeSortAndOrderMethod(historicTaskInstanceQuery);
        // 执行分页
        historicTaskInstanceList = page.executePage(historicTaskInstanceQuery, HistoricTaskInstance.class);
        // 结果适配为FlowTask对象
        List<FlowTask> flowTasks = Lists.newLinkedList();
        for (HistoricTaskInstance taskInstance : historicTaskInstanceList) {
            flowTasks.add(new FlowTask(taskInstance));
        }
        page.setRows(flowTasks);
        return page;
    }


    /**
     * 获取待办列表
     *
     * @param userId 用户ID
     * @param page
     * @return
     */
    public Page<FlowTask> getToDoTasks(String userId, Page<FlowTask> page) {
        checkArgument(StringUtils.isNotEmpty(userId), "userId must not be null.");
        // 获取当前用户的待办任务
        TaskQuery taskQuery = taskService.createTaskQuery().taskCandidateOrAssigned(userId);
        List<org.activiti.engine.task.Task> taskList = null;
        if (page == null) {
            page = new Page<>();
            page.setOrder(Page.ORDER_DIRECTION_ASC);
            page.setSort(Page.DEFAULT_SORT);
        }
        taskQuery = page.executeSortAndOrderMethod(taskQuery);
        taskList = page.executePage(taskQuery, org.activiti.engine.task.Task.class);
        List<FlowTask> flowTasks = Lists.newLinkedList();
        for (org.activiti.engine.task.Task task : taskList) {
            flowTasks.add(new FlowTask((TaskEntity) task));
        }
        page.setRows(flowTasks);
        return page;
    }

    /**
     * 任务办理
     *
     * @param taskId            任务ID
     * @param nextTaskAssignees 下一步办理人员，可以是用户组ID，也可以是用户ID，最终由配置流程时的变量决定
     * @param comment           办理意见
     * @param userId            办理用户ID
     * @param variables         任务变量
     * @return
     */
    public FlowTask doTask(String taskId, String nextTaskAssignees, String comment, String userId, Map<String, Object> variables) {
        TaskEntity task = getTaskByTaskId(taskId);
        checkState(task != null, "找不到任务id为{}的任务信息", taskId);
        if (StringUtils.isNotEmpty(comment)) {
            taskService.addComment(taskId, task.getProcessInstanceId(), comment);
        }
        Map<String, Object> processVariables = FlowService.taskService().getTaskVariables(taskId);
        Map<String, Object> totalVariables = Maps.newHashMap();
        totalVariables.putAll(processVariables);
        totalVariables.putAll(variables);
        List<FlowCondition> flowConditions = Lists.newArrayList();
        FlowCondition flowCondition = null;
        for (Map.Entry<String, Object> entry : totalVariables.entrySet()) {
            flowCondition = new FlowCondition();
            flowCondition.setVarName(entry.getKey());
            flowCondition.setVarValue(Objects.toString(entry.getValue()));
            flowConditions.add(flowCondition);
        }
        // 指定下一步办理人。
        if (StringUtils.isNotEmpty(nextTaskAssignees)) {
            FlowActivity nextFlowActivity = FlowService.activityService().getNextActivityInfo(taskId, flowConditions);
            List<String> candidates = Splitter.on(",").splitToList(nextTaskAssignees);
            FlowService.taskService().setNextUserVariables(candidates, nextFlowActivity.getAssignee().getValue(), nextFlowActivity.getMultiInstanceVariableName(), variables);
        }
        // 如果下一个节点是接受任务，则需要发送信号通知接受任务执行
        ReceiveTask receiveTask = FlowService.activityService().findNextReceiveTask(taskId);
        if (receiveTask != null) {
            Execution execution = runtimeService.createExecutionQuery().activityId(receiveTask.getId()).singleResult();
            runtimeService.trigger(execution.getId());
        }
        // 如果当前节点的办理人是候选，则需要先签收
        try {
            taskService.claim(taskId, userId);
        } catch (Exception e) {
            LOGGER.error("签收失败", e);
        }
        // 判断是否是委托任务，如果是则需要执行委托任务
        String owner = task.getOwner();
        if (StringUtils.isNotEmpty(owner) && !StringUtils.equals(owner, "null")) {
            DelegationState delegationState = task.getDelegationState();
            if (delegationState.equals(DelegationState.RESOLVED)) {//委托任务已解决
                throw new BadRequestException("任务ID为 " + taskId + " 的任务已经被解决");
            } else if (delegationState.equals(DelegationState.PENDING)) {
                taskService.resolveTask(taskId, variables);
            }
        }
        taskService.complete(taskId, variables);
        TaskEntity nextTask = getLastExecutionTask(task.getProcessInstanceId());
        if (nextTask == null) {//办理完成
            return null;
        }
        return new FlowTask(nextTask);
    }

    /**
     * 任务委托。
     *
     * @param taskId   被委托的任务ID
     * @param assignee 委托人用户ID
     * @return
     */
    public boolean delegateTask(String taskId, String assignee) {
        checkArgument(StringUtils.isNotEmpty(taskId), "taskId must not be null.");
        checkArgument(StringUtils.isNotEmpty(assignee), "the delegated userid must not be null.");
        try {
            taskService.delegateTask(taskId, assignee);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 根据任务ID获取任务信息
     *
     * @param taskId
     * @return
     */
    @Cacheable("flowCache")
    public TaskEntity getTaskByTaskId(String taskId) {
        checkArgument(StringUtils.isNotEmpty(taskId), "taskId must not be null.");
        org.activiti.engine.task.Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task != null) {
            return (TaskEntity) task;
        }
        return null;
    }

    /**
     * 根据executionId获取任务信息
     *
     * @param executionId
     * @return
     */
    public TaskEntity getTaskByExecutionId(String executionId) {
        checkArgument(StringUtils.isNotEmpty(executionId), "executionId must not be null.");
        return (TaskEntity) taskService.createTaskQuery().executionId(executionId).singleResult();
    }

    /**
     * 根据当前办理任务ID获取下一节点任务信息
     *
     * @param taskId     任务ID或者流程定义ID
     * @param conditions 排他网关的条件语句
     * @return
     */
    public Task getNextTask(String taskId, List<FlowCondition> conditions) {
        ExecutionEntity executionEntity = getExecutionByTaskId(taskId);
        String definitionId = executionEntity == null ? taskId : executionEntity.getProcessDefinitionId();
        String instanceId = executionEntity == null ? definitionId : executionEntity.getProcessInstanceId();
        FlowNode currentFlowElement = FlowService.activityService().getFlowNode(taskId);
        BpmnModel bpmnModel = FlowService.activityService().getBpmnModelByDefinitionId(definitionId);
        // 获取下一节点任务信息
        Task resultTask = getNextTask(currentFlowElement, conditions, instanceId, bpmnModel);
        return resultTask;
    }

    /**
     * 获取下一节点任务信息
     *
     * @param currentFlowElement     当前流程活动信息
     * @param conditions             排他网关的判断条件
     * @param definitionOrInstanceId 如果是开始节点，则是流程定义ID，否则是流程实例ID
     * @return
     */
    private Task getNextTask(FlowElement currentFlowElement, List<FlowCondition> conditions, String definitionOrInstanceId, BpmnModel bpmnModel) {
        List<SequenceFlow> sequenceFlows = Lists.newArrayList();
        if (currentFlowElement instanceof SequenceFlow) {
            sequenceFlows.add((SequenceFlow) currentFlowElement);
        } else {
            FlowNode flowNode = (FlowNode) currentFlowElement;
            // 获取当前节点流出去的线
            sequenceFlows = flowNode.getOutgoingFlows();
        }
        for (SequenceFlow sequenceFlow : sequenceFlows) {
            // 获取顺序流的下一节点
            FlowNode nextFlowNode = (FlowNode) sequenceFlow.getTargetFlowElement();
            // 如果下一节点是排他网关
            if (nextFlowNode instanceof ExclusiveGateway) {
                checkState(conditions != null && !conditions.isEmpty(), "下一节点是排他网关，必须根据网关输出流条件来获取后续节点，所以输出流条件不能为空！");
                //获取排他网关的后续线路
                List<SequenceFlow> exclusiveSequenceFlows = nextFlowNode.getOutgoingFlows();
                if (exclusiveSequenceFlows.size() == 1) {
                    return getNextTask(exclusiveSequenceFlows.get(0), conditions, definitionOrInstanceId, bpmnModel);
                } else {
                    Map<String, Object> variables = FlowService.instanceService().getStartVariables(definitionOrInstanceId);
                    for (SequenceFlow exclusiveSequenceFlow : exclusiveSequenceFlows) {
                        String expression = exclusiveSequenceFlow.getConditionExpression();
                        if (FlowUtils.checkCondition(expression, conditions, variables)) {
                            return getNextTask(exclusiveSequenceFlow, conditions, definitionOrInstanceId, bpmnModel);
                        }
                    }
                }
            } else if (nextFlowNode instanceof UserTask) {//如果是用户任务
                return (UserTask) nextFlowNode;
            }
        }
        return null;
    }

    /**
     * 根据执行ID获取执行操作对象
     *
     * @param executionId
     * @return
     */
    public ExecutionEntity getExecutionByExecutionId(String executionId) {
        checkArgument(StringUtils.isNotEmpty(executionId), "executionId must not be null.");
        return (ExecutionEntity) runtimeService.createExecutionQuery().executionId(executionId).singleResult();
    }

    /**
     * 根据任务ID获取执行对象
     * <br/>
     * 如果根据任务ID获取不到任务对象，则可能是开始事件，参数是流程定义ID
     *
     * @param taskId 任务ID或者开始任务ID
     * @return
     */
    public ExecutionEntity getExecutionByTaskId(String taskId) {
        TaskEntity taskEntity = FlowService.taskService().getTaskByTaskId(taskId);
        if (taskEntity == null) {
            ProcessInstance instance = FlowService.instanceService().getProcessInstanceByInstanceId(taskId);
            if (instance != null) {
                return (ExecutionEntity) instance;
            }
        } else {
            String executionId = taskEntity.getExecutionId();
            return getExecutionByExecutionId(executionId);
        }
        return null;
    }

    /**
     * 根据历史任务ID获取办理意见
     *
     * @param historyTaskId
     * @return
     */
    public Comment getTaskComment(String historyTaskId) {
        List<Comment> comments = taskService.getTaskComments(historyTaskId);
        if (comments != null && comments.size() > 0) {
            return comments.get(0);
        } else {
            return null;
        }
    }

    /**
     * 获取最新的可执行任务。
     * <br/>
     * 如果在当前任务完成之后，调用该方法可以获取到接下来待完成的可执行任务节点。
     * <br/>
     * 一般通过instanceId获取Execution，可以获取到2个，一个是ProcessInstance对象，一个是未执行的任务对象。
     *
     * @param instanceId
     * @return
     */

    public TaskEntity getLastExecutionTask(String instanceId) {
        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(instanceId).list();
        if (executions == null || executions.size() <= 1) {//说明只有ProcessInstance对象
            return null;
        } else {
            ExecutionEntity executionEntity = (ExecutionEntity) executions.get(executions.size() - 1);
            TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().executionId(executionEntity.getId()).singleResult();
            return taskEntity;
        }
    }

    /**
     * 设置选择的下一步办理用户
     * <br/>
     * 设置步骤如下所示：
     * <ul>
     * <li>如果可选用户只有一个，并且assygneeType==singleUser或者selectSingleUser，则设置为可选用户集合中的第一个元素</li>
     * <li>如果可选用户是多个，并且assignee=multiUser/selectMultiUser/singleGroup/selectSingleGroup/multiGroup/selectMultiGroup，则作为候选用户，设置集合</li>
     * <li>如果可选用户是多个，并且后续节点有多实例变量名，则认为是多实例任务，则设置多实例变量为可选用户集合</li>
     * </ul>
     *
     * @param candidates   可选用户集合
     * @param assigneeType
     * @param varialbes
     */
    public void setNextUserVariables(List<String> candidates, String
            assigneeType, String multiInstanceVariable, Map<String, Object> varialbes) {
        if (StringUtils.isNotEmpty(multiInstanceVariable)) {//如果是多实例，则设置多实例变量
            varialbes.put(multiInstanceVariable, candidates);
        } else if (StringUtils.equals(assigneeType, FlowConstants.Assignee.SELECTSINGLEUSER.getAssignee()) || StringUtils.equals(assigneeType, FlowConstants.Assignee.SINGLEUSER.getAssignee())) {
            varialbes.put(assigneeType, candidates.get(0));
        } else {
            varialbes.put(assigneeType, candidates);
        }
    }

    /**
     * 获取任务变量
     *
     * @param currentTaskId
     * @return
     */
    public Map<String, Object> getTaskVariables(String currentTaskId) {
        Map<String, Object> variables = Maps.newHashMap();
        TaskEntity taskEntity = getTaskByTaskId(currentTaskId);
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(currentTaskId).singleResult();
        currentTaskId = taskEntity == null ? (historicTaskInstance == null ? "" : historicTaskInstance.getId()) : taskEntity.getId();
        String processInstanceId = taskEntity == null ? (historicTaskInstance == null ? "" : historicTaskInstance.getProcessInstanceId()) : taskEntity.getProcessInstanceId();
        if (StringUtils.isNotEmpty(currentTaskId)) {
            ExecutionEntity executionEntity = getExecutionByTaskId(currentTaskId);
            if (executionEntity != null) {
                variables.putAll(executionEntity.getProcessVariables());
            }
            List<HistoricVariableInstance> variableInstances = historyService.createHistoricVariableInstanceQuery().taskId(currentTaskId).list();
            if (variableInstances != null && variableInstances.size() > 0) {
                for (HistoricVariableInstance variableInstance : variableInstances) {
                    variables.put(variableInstance.getVariableName(), variableInstance.getValue());
                }
            }
            if (StringUtils.isNotEmpty(processInstanceId)) {
                variableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list();
                if (variableInstances != null && variableInstances.size() > 0) {
                    for (HistoricVariableInstance variableInstance : variableInstances) {
                        variables.put(variableInstance.getVariableName(), variableInstance.getValue());
                    }
                }
            }
        }

        return variables;
    }

    /**
     * 根据流程实例ID获取该流程实例的历史任务信息
     *
     * @param processId
     * @return
     */
    public List<HistoricTaskInstance> getHistoricTaskInstanceByProcessId(String processId) {
        return historyService.createHistoricTaskInstanceQuery().processInstanceId(processId).list();
    }

    /**
     * 获取流程执行全局变量
     *
     * @param executionId
     * @return
     */
    public Map<String, Object> getExecutionVariables(String executionId) {
        return runtimeService.getVariables(executionId);
    }

    /**
     * 获取某个任务节点设置的办理用户，可能是固定用户ID，也可能是变量
     *
     * @param task
     * @return
     */
    public FlowAssignee getTaskSelectAssignee(Task task) {
        FlowAssignee flowAssignee = null;
        if (task != null && task instanceof UserTask) {
            UserTask userTask = (UserTask) task;
            String assignee = userTask.getAssignee();
            List<String> candidateGroups = userTask.getCandidateGroups();
            List<String> candidateUsers = userTask.getCandidateUsers();
            if (StringUtils.isNotEmpty(assignee)) {
                flowAssignee = new FlowAssignee();
                flowAssignee.setField(FlowAssignee.Field.Assignee);
                if (StringUtils.startsWith(assignee, "${") && StringUtils.endsWith(assignee, "}")) {//说明需要动态指派人
                    assignee = StringUtils.substringBetween(assignee, "${", "}");
                    flowAssignee.setValue(assignee);
                    if (StringUtils.startsWith(assignee, "select")) {//需要选择
                        flowAssignee.setValueType(FlowAssignee.ValueType.Select);
                    } else {//不需要选择，由监听器生成
                        flowAssignee.setValueType(FlowAssignee.ValueType.NoSelect);
                    }
                } else {//固定的用户ID
                    flowAssignee.setValueType(FlowAssignee.ValueType.Constant);
                    flowAssignee.setValue(assignee);
                }
            } else if (candidateUsers != null && candidateUsers.size() > 0) {
                String candidateUser = candidateUsers.get(0);
                flowAssignee = new FlowAssignee();
                flowAssignee.setField(FlowAssignee.Field.CandidateUser);
                if (StringUtils.startsWith(candidateUser, "${") && StringUtils.endsWith(candidateUser, "}")) {//说明需要动态指派人
                    candidateUser = StringUtils.substringBetween(candidateUser, "${", "}");
                    flowAssignee.setValue(candidateUser);
                    if (StringUtils.startsWith(candidateUser, "select")) {//需要选择
                        flowAssignee.setValueType(FlowAssignee.ValueType.Select);
                    } else {
                        flowAssignee.setValueType(FlowAssignee.ValueType.NoSelect);
                    }
                } else {
                    flowAssignee.setValue(Joiner.on(",").join(candidateUsers));
                    flowAssignee.setValueType(FlowAssignee.ValueType.Constant);
                }
            } else if (candidateGroups != null && candidateGroups.size() > 0) {
                String candidateGroup = candidateGroups.get(0);
                flowAssignee = new FlowAssignee();
                flowAssignee.setField(FlowAssignee.Field.CandidateGroup);
                if (StringUtils.startsWith(candidateGroup, "${") && StringUtils.endsWith(candidateGroup, "}")) {//说明需要动态指派人
                    candidateGroup = StringUtils.substringBetween(candidateGroup, "${", "}");
                    flowAssignee.setValue(candidateGroup);
                    if (StringUtils.startsWith(candidateGroup, "select")) {//需要选择
                        flowAssignee.setValueType(FlowAssignee.ValueType.Select);
                    } else {
                        flowAssignee.setValueType(FlowAssignee.ValueType.NoSelect);
                    }
                } else {
                    flowAssignee.setValue(Joiner.on(",").join(candidateGroups));
                    flowAssignee.setValueType(FlowAssignee.ValueType.Constant);
                }
            }
        }
        return flowAssignee;
    }


    /**
     * 获取下一步任务的需要选择的办理人员
     * <br/>
     * 根据需先定义，如果节点的办理人员是上一办理节点指定的，则在设计流程时会设置办理人员为${code ${selectXXX}}
     * <br/>
     * 例如，如果选择单个用户，则是{@code ${selectSingleUser}}
     *
     * @param currentTaskId 当前任务/事件ID
     * @param conditions    输出流判断条件
     * @return
     */
    public FlowAssignee getNextTaskSelectAssignee(String currentTaskId, List<FlowCondition> conditions) {
        Task task = getNextTask(currentTaskId, conditions);
        if (task == null) {
            return new FlowAssignee();
        }
        return getTaskSelectAssignee(task);
    }

    public FlowAssignee getNextTaskSelectAssignee(Task nextTask) {
        if (nextTask == null) {
            LOGGER.warn("next task is null,maybe is end event.");
            return null;
        }
        return getTaskSelectAssignee(nextTask);
    }
}
